package cn.agiledata.bank.barcodepayment.logic;

import java.math.BigDecimal;

import cn.agiledata.bank.barcodepayment.exception.BarCodePayException;
import cn.agiledata.bank.common.util.StringUtil;

public abstract class AbstractBarCode implements BarCode {
    /**
     * 榛樿鐨勫姞鏉冨洜瀛愩?i=1,2,...,32锛學32=3
     */
    private static final int[] defaultWeightedFactors = { 28, 61, 74, 85, 57,
            93, 19, 31, 71, 75, 56, 25, 51, 73, 17, 89, 30, 62, 45, 53, 15, 50,
            5, 49, 34, 81, 76, 27, 90, 9, 30, 3 };

    private String value;

    public String getValue() {
        return this.value;
    }

    public void setValue(String value) throws BarCodePayException {
        this.value = value;
        try {
            validate(value);
        } catch (BarCodePayException e) {
            this.value = null;
            throw e;
        }
    }

    /**
     * 鏍规嵁閲戦浣嶄簬鏉″舰鐮佷腑鐨勮捣濮嬩綅缃拰缁堟浣嶇疆鑾峰彇閲戦锛屼綅缃储寮曚粠1寮?璁?
     * 
     * @param beginIndex
     *            閲戦浣嶄簬鏉″舰鐮佷腑鐨勮捣濮嬩綅缃?
     * @param endIndex
     *            閲戦浣嶄簬鏉″舰鐮佷腑鐨勭粓姝綅缃?
     * @return 閲戦
     */
    protected final BigDecimal getAmount(int beginIndex, int endIndex) {
        String amountValue = this.value.substring(beginIndex - 1, endIndex);
        return new BigDecimal(amountValue).divide(new BigDecimal(100), 2,
                BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 鏍￠獙鏉″舰鐮佸?鏍煎紡鏄惁姝ｇ‘
     * 
     * @param barCodeValue
     *            鏉″舰鐮佸?
     * @throws BarCodePayException
     *             濡傛灉鍦ㄦ牎楠岃繃绋嬩腑鍑虹幇閿欒
     */
    protected void validate(String barCodeValue) throws BarCodePayException {
        if (!StringUtil.isNumber(barCodeValue)) {
            throw BarCodePayException.ERROR_NOTNUMBER;
        }
    }

    /**
     * 鑾峰彇鎸囧畾鏁板瓧鍨嬪瓧绗︿覆涓殑鎸囧畾绱㈠紩浣嶇疆鐨勬暟瀛楋紝浣嶇疆绱㈠紩浠?寮?璁?
     * 
     * @param s
     *            鏁板瓧鍨嬪瓧绗︿覆
     * @param index
     *            绱㈠紩浣嶇疆
     * @return 鎸囧畾鏁板瓧鍨嬪瓧绗︿覆涓殑鎸囧畾绱㈠紩浣嶇疆鐨勬暟瀛?
     */
    protected static final int intOf(String s, int index) {
        String i = s.substring(index - 1, index);
        return Integer.valueOf(i).intValue();
    }

    /**
     * 鏍规嵁銆婂叕鐢ㄤ簨涓氭敹璐瑰笎鍗曠紪鐮佸簲鐢ㄨ鑼冦?涓婃捣鍦版柟鏍囧噯DB31/T 291-2003鐨勯檮褰旴涓殑鍏紡锛岃绠楀姞鏉冨洜瀛?
     * 
     * @param i
     *            瑕佽绠楃殑鍔犳潈鍥犲瓙绱㈠紩
     * @param maxIndex
     *            鏈?ぇ绱㈠紩
     * @param maxIndexWeightedFactor
     *            鏈?ぇ绱㈠紩澶勭殑鍔犳潈鍥犲瓙鍊?
     * @return 鍔犳潈鍥犲瓙
     */
    private static final int weightedFactor(int i, int maxIndex,
            int maxIndexWeightedFactor) {
        if (i > maxIndex) {
            return 0;
        } else if (i == maxIndex) {
            return maxIndexWeightedFactor;
        } else if (maxIndex == 32 && maxIndexWeightedFactor == 3) {
            // 榛樿鎯呭喌涓嬶紝W32=3锛屽鏋滄槸杩欑鎯呭喌锛岀洿鎺ヤ粠榛樿鐨勫姞鏉冨洜瀛愭暟缁勪腑鍙栧?锛屼互鎻愰珮閫熷害
            return defaultWeightedFactors[i - 1];
        } else {
            return (weightedFactor(i + 1, maxIndex, maxIndexWeightedFactor) * 10) % 97;
        }
    }

    /**
     * 鏍规嵁銆婂叕鐢ㄤ簨涓氭敹璐瑰笎鍗曠紪鐮佸簲鐢ㄨ鑼冦?涓婃捣鍦版柟鏍囧噯DB31/T 291-2003鐨勯檮褰旴涓殑鍏紡锛岃绠楁牎楠岀爜
     */
    protected static final String checkCode(String barCodeValue) {
        int checkValue = 0;
        int maxCheckLength = 32; // 鏈?ぇ鏍￠獙闀垮害
        int length = barCodeValue.length() > maxCheckLength ? maxCheckLength
                : barCodeValue.length();
        for (int i = 1; i <= length; i++) {
            checkValue += intOf(barCodeValue, i)
                    * weightedFactor(i, maxCheckLength, 3); // 姝ゅ閲囩敤榛樿鐨勫姞鏉冨洜瀛愶紝W32=3
        }
        checkValue = 98 - checkValue % 97;
        return checkValue < 10 ? "0" + checkValue : "" + checkValue;
    }
}
